#!/usr/bin/python
# File: power.py


# Copyright (c) 2010 by diacus (Diego R. Guzman)
#
# GNU General Public Licence (GPL)
# 
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
# Place, Suite 330, Boston, MA  02111-1307  USA
#

import re
import os
import commands
import ConfigParser
from optparse import OptionParser
from rangeparser import *

# Definig REGEXP
number_re = "([1-4]\\d?|5[0-4]?|[6-9])"
range_re  = "(" + number_re + "-" + number_re + ")"
token_re  = "(" + range_re + "|" + number_re + ")"
list_re  = "^" + token_re + "(," + token_re + ")*$"
hexa_re = "[\\da-f]{2}"
macaddress_re = "^%s(:%s){5}$" % (hexa_re,hexa_re)

def isAddress( line ):
    """Parses the line string, and returns true if the text matches with the hostprefix
    previously defined."""
    return True if re.match( "^" + hostprefix, line ) else False

def isMAC( address ):
    """Parses the address string, and returns true if the text matches with the mac address
    pattern previously defined."""
    return True if re.match( macaddress_re, address ) else False

def getMACs( file ):
    """Reads a file that contains the mac address list asociated to the all host's names
    in the lab."""
    dirs = {}
    fmac = open( file, "r" )
    macs = filter( isAddress, fmac.readlines() )
    fmac.close()
    for mac in macs:
         address = re.split("\\s+",mac)[1]
         if isMAC( address ) :
             dirs[re.split("\\s+",mac)[0]] = address
    return dirs

def isList( input ):
    """Returns true if input matches with the list_re pattern"""
    return True if re.match(list_re,input) else False

def isRange( input ):
    """Returns true if input matches with the range_re pattern"""
    return True if re.match("^" + range_re + "$" ,input) else False

def isNumber( input ):
    """Returns true if input matches with the number_re pattern"""
    return True if re.match("^" + number_re + "$" ,input) else False


def expand( range_limits ):
    """Tekes the interval given by range_limits and expand it:
    Example:
        the interval 1-4 changes to the list [1,2,3,4]
    """
    begin, end = int( range_limits.split("-")[0] ), int( range_limits.split("-")[1] )
    if begin > end :
        print "Error: The range [", begin, ",", end, "] is emtpy."
        begin, end = end, begin
    return range(begin,end + 1)

def genList( input ):
    """Convert an interval string to the equivalent list.
    Example:
        This function converts the range 1-3,5-8 to the list [1,2,3,5,6,7,8]
    """
    if isList( input ) :
        subranges = input.split(",")
        list_range = map ( int, filter( isNumber, subranges ) )
        for range_limits in  map( expand, filter( isRange, subranges ) ):
            list_range.extend( range_limits )
        list_range.sort()
        onelist = list ( set ( list_range ) )
        machines = ""
        for m in onelist: machines += "%s%d\n" % (hostprefix,m)
        farch = open( tmpfile, "w")
        try:
            farch.write(machines)
        except:
            print "Error: Can't write in file", tmpfile
            farch.close()
    
    else:
        print "Error: Invalid range"
        exit(1)

    return map(getName, onelist)

def getName( n ):
    """Returns the hostprefix string concatenated with the integer n"""
    return hostprefix + str(n)

thispath = "/etc/powerlab"

# The power.conf file contains que program's initial state
configfile = os.path.join( thispath, "power.conf")
configuration = ConfigParser.RawConfigParser()
configuration.read(configfile)

hostprefix = configuration.get( 'General', 'hostprefix' )
macsfile = configuration.get( 'General', 'maclist' )
tmpfile = configuration.get( 'General', 'tmplist' )
default_group = configuration.get( 'Groups', 'default_group' )

# This section parses the command line arguments
parser = OptionParser()
parser.add_option("-d","--down", action="store_true", dest="down",help="Option for turn off the selected machines")
parser.add_option("-u","--up", action="store_true", dest="up",help="Option for turn on the selected machines")
parser.add_option("-a","--all", action="store_true", dest="all",help="Select all machines")
parser.add_option("-r","--range",dest="range",help="Select machines by range")
parser.add_option("-g","--group",dest="group",help="Select machines by group")

options, args = parser.parse_args()

def main():

    if not (options.range or options.group or options.all ) :
        print "Error: Selection mode unspecified. Please choose range or group mode."
        parser.print_help()
        exit(1)	
    elif options.range != None:
        list = genList(options.range)

    if options.down == options.up :
        print "Error: Action unspecified. Please choose up or down."
        parser.print_help()
        exit(1)
    else:
        if options.down:
            if options.all :
                command = "dsh -cMg lab /sbin/shutdown -h now"
            elif options.group :
                command = "dsh -cMg %s /sbin/shutdown -h now" % (options.group) 
            elif options.range :
                command = "dsh -cMf %s /sbin/shutdown -h now" % (tmpfile) 
        else:
            macs = getMACs( os.path.join( thispath, macsfile ) )
            if options.group :
                try:
                    selected_group = configuration.get( 'Groups', options.group )
                    list  = genList( selected_group )
                except:
                    print "Error: Group undefined (%s)." % (options.group)
                    exit(1)
            
            elif options.all :
                list  = genList( default_group )
            
            command = "wakeonlan "
            for pc in list :
                try:
                    command += " %s" % (macs[pc])
                except:
                    print "Error: The machine:", pc, "is not available."
            
        commands.getstatusoutput(command)
    return 0
	    
main()
